﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;

namespace library
{
    public static class Utils
    {
        static MD5 MD5 = MD5.Create();

        static SHA256 SHA = SHA256.Create();

        static Crc16 CRC = new Crc16(Crc16Mode.Standard);

        internal static Random Rand = new Random();

        public static byte[] GetPseudoAddress()
        {
            return GetAddress(16);
        }

        public  static byte[] GetAddress(int size = 0)
        {
            if (size == 0)
                size = Parameters.addressSize;

            byte[] result = new byte[size];

            Rand.NextBytes(result);

            return result;
        }

        internal static string Points(IEnumerable<byte> data)
        {
            List<string> s = new List<string>();

            foreach (byte b in data)
            {
                s.Add(b.ToString().PadLeft(3, '0'));
            }

            return string.Join(".", s);
        }

        public static byte[] ComputeCRC(byte[] data)
        {
            return CRC.ComputeChecksumBytes(data);
        }

        public static byte[] ComputeHash(byte[] buffer, int offset, int count)
        {
            lock (MD5)
                return MD5.ComputeHash(buffer, offset, count);
        }
        public static string ToAddressSizeBase64String(string value)
        {
            var bytes = Encoding.Unicode.GetBytes(value);

            byte[] hash;

            lock (SHA)
                hash = SHA.ComputeHash(bytes, 0, bytes.Length);
            
            return Utils.ToBase64String(hash);
        }

        public static byte[] ReadBytes(Stream stream)
        {
            byte[] buffer = new byte[4];
            
            stream.Read(buffer, 0, buffer.Length);

            int length = BitConverter.ToInt32(buffer, 0);

            byte[] result = new byte[length];

            stream.Read(result, 0, length);

            return result;
        }

        public static byte[] ReadBytes(byte[] data, int offset = 0)
        {
            if(offset >= data.Length)
                return new byte[0];
                 
            byte[] buffer = new byte[4];

            int length = BitConverter.ToInt32(data, offset);

            return data.Skip(buffer.Length + offset).Take(length).ToArray();
        }

        public static string ToBase64String(byte[] term)
        {
            return term == null ? null : Convert.ToBase64String(term).Replace('/', '-').Replace('+', '(');
        }

        public static string DisplayBytes(long byteCount)
        {
            string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
            if (byteCount < 1)
                return "0" + suf[0];
            long bytes = Math.Abs(byteCount);
            int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
            double num = Math.Round(bytes / Math.Pow(1024, place), 1);
            return string.Format("{0:n1} {1}", (Math.Sign(byteCount) * num), suf[place]);
        }

        public static void AppendAllBytes(string filename, byte[] data)
        {
            using (var stream = new FileStream(filename, FileMode.Append))
            {
                stream.Write(data, 0, data.Length);
            }
        }

        public static int GetAvaiablePort()
        {
            IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();

            IPEndPoint[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners();

            int port = 0;

            while (port == 0)
            {
                port = Rand.Next(10000, 50000);

                if (tcpConnInfoArray.Any(x => x.Port == port))
                    port = 0;
            }

            return port;
        }

        public static bool IsDirectory(string path)
        {
            FileAttributes attr = File.GetAttributes(path);

            return (attr & FileAttributes.Directory) == FileAttributes.Directory;
        }

        public static byte[] FromBase64String(string base64String)
        {
            if (base64String == null)
                return null;
                     
            if (base64String.Length != Parameters.base64AddressSize)
                return null;

            try
            {
                return Convert.FromBase64String(base64String.Replace('-', '/').Replace('(', '+'));
            }
            catch { }

            return null;
        }



        public static Stream ToStream(this Image image, ImageFormat formaw)
        {
            var stream = new MemoryStream();
            image.Save(stream, formaw);
            stream.Position = 0;
            return stream;
        }

        public static string GetMimeType(string fileName)
        {
            string mimeType = "application/unknown";
            string ext = System.IO.Path.GetExtension(fileName).ToLower();
            Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
            if (regKey != null && regKey.GetValue("Content Type") != null)
                mimeType = regKey.GetValue("Content Type").ToString();
            return mimeType;
        }
    }

    public enum Crc16Mode : ushort { Standard = 0xA001, CcittKermit = 0x8408 }

    public class Crc16
    {
        readonly ushort[] table = new ushort[256];

        public ushort ComputeChecksum(params byte[] bytes)
        {
            ushort crc = 0;
            for (int i = 0; i < bytes.Length; ++i)
            {
                byte index = (byte)(crc ^ bytes[i]);
                crc = (ushort)((crc >> 8) ^ table[index]);
            }
            return crc;
        }

        public byte[] ComputeChecksumBytes(params byte[] bytes)
        {
            ushort crc = ComputeChecksum(bytes);
            return BitConverter.GetBytes(crc);
        }

        public Crc16(Crc16Mode mode)
        {
            ushort polynomial = (ushort)mode;
            ushort value;
            ushort temp;
            for (ushort i = 0; i < table.Length; ++i)
            {
                value = 0;
                temp = i;
                for (byte j = 0; j < 8; ++j)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                table[i] = value;
            }
        }
    }

}
